perm filename INCMP.MAC[IP,SYS] blob
sn#680191 filedate 1982-10-14 generic text, type T, neo UTF8
;CWL:<403-INET>INCMP.MAC.40303 25-Apr-82 13:30:04, Edit by CLYNN
; Increased MAXGWA, removed MAXROU, allow "." between address bytes
; Added PURGEs, use partial gateway if too many addresses,
; Error if no gateway type, set IP seg id & ICMP seq fields
; Save TCB & call PRNPKT in ICMDSP, add timestamp & information requests
; Pass parameter problem pointer to ICMERR in T2
SEARCH INPAR,PROLOG
IFN MNET,<
SEARCH MNTPAR
>
TTITLE INCMP
SUBTTL Internet Control Message Protocol, Dan Tappan, 1/82
SWAPCD
COMMENT !
These routines impliment the Internet Control Message
Protocal. They are derived from the old GGP routines,
which protocal we no longer support.
Besides handling the protocal messages this module also
maintains the gateway tables (as distinguished from the
routing tables).
Routines in this module
ICMINI...... Initialize the ICMP handler*
GWYINI...... Initialize the gateway tables*
LODFIL...... Load the gateway file
PRCLIN...... Process lines in the gateway file
LOADGW...... Load a gateway descriptor
DEFGWY...... Create gateway blocks from a gateway descriptor
ICMPRC...... Top level ICMP process.*
ICMDSP...... Process a single ICMP message*
ICMERR...... Send an ICMP error message*
!
; Accumulators used globally in this module:
DEFAC(GW,BFR) ; Points to a gateway block
DEFAC(CPKT,TPKT) ; Index register to point to ICMP pkt
; Parameters:
MAXGWA==↑D50 ; Number of GWs we will keep track of
; (Gateways and multi-homed hosts)
;MAXROU==↑D40 ; Number of routing names
; The file name to use:
IFKA < GWFILE: ASCIZ "<SYSTEM>INTERNET.GATEWAYS">
IFNKA < GWFILE: ASCIZ "SYSTEM:INTERNET.GATEWAYS">
;;; ICMP packet is pointed to by CPKT, structure as defined in
;;; INPAR
MINICW==PKTELI+<<MINIHS+3>/4>+2 ; Minimum ICMP packet size, words with local
MINIHB==<MINICW-PKTELI>*4 ; Usual header size, w/o imbedded pkt
; Structure of a Gateway block:
DEFSTR(GWUP,0,0,1) ; Gateway should be useable
DEFSTR(GWPIP,0,1,1) ; Ping in progress
DEFSTR(GWTYP,0,5,4) ; Gateway type
GW%PRM==1 ; Prime: Speaks ICMP
GW%DUM==2 ; Dumb: Fwd's pkts, but that's all
GW%HST==3 ; Host: Avoid except in emergency
GW%AUP==4 ; Always-up: Fwds but doesn't reflect
DEFSTR(GWHIS,0,14,7) ; Ping history bits
DEFSTR(GWSPC,0,17,3) ; Successful ping count
.THRUP==<6*WID(GWHIS)>/8 ; Threshold to say it is up
.THRDN==<3*WID(GWHIS)>/8 ; Threshold to say it is down
IFL <<1←WID(GWSPC)>-1>-WID(GWHIS), PRINTX ?GWSPC too small
DEFSTR(GWICT,0,35,18) ; Interface count
.GWILS==1 ; Where in block to find the list
;NB: The FIRST ENTRY in the list is on a connected net
MXGWIC==6 ; Maximum number of interfaces
GWBKSZ==.GWILS+MXGWIC ; Size of a gateway block
; Tables kept and used by ICMP
;GWTAB is a list of pointers to gateway blocks that we know about.
;NR(GWTAB,1) ; Pointer to the table
;ICMINI Initialize ICM Protocol
;
; CALL ICMINI
;Ret+1: Always.
ICMINI::SETZM PINGTM ; Do Pings now
SETZM ICMTIM ; Run ICMP now
MOVE T1,NETHT0 ; Get hash table clear interval
ADD T1,TODCLK ; add to now
MOVEM T1,NETHTM ; when to clear them again
SKIPE ICMIPQ ; Already have a queue head?
JRST ICMIN0 ; Yes.
MOVEI T1,QSZ ; Size of a queue head
CALL GETBLK ; Get one from free area
JUMPE T1,[INBUG(HLT,<No storage for ICMP>,ICMNST)]
MOVEM T1,ICMIPQ ; Put where we can find it
CALL INITQ ; Initialize it
ICMIN0:
SETOM ICMON ; Turn the protocal on
; CALLRET GWYINI ; Init gateways and return
; Fall into GWYINI
;;; GWYINI -- Initialize the gateway tables.
;;; Called via
;;; CALL GWYINI
;;; Rets +1 always
GWYINI::SE1CAL ; In case called from MDDT
ACVAR <I>
PUSH P,GW ; Save an AC
SKIPN GWTAB ; Is this a reinit?
JRST GWYIN2 ; No.
; Reinit
MOVSI I,-MAXGWA ; Set to scan GWTAB
GWYINL: MOVE GW,GWTAB
ADDI GW,0(I) ; Point to actual entry
SETZ T1, ; Get a zero
EXCH T1,0(GW) ; Flush entry, get previous value
SKIPN T1 ; Was there one?
JRST GWYIN1 ; No, continue
;;; Signal that all these gateways are down
PUSH P,T1 ; Save the block
MOVE T1,.GWILS(T1) ; get local address of this gateway
CALL GWYDWN ; Signal that it's gone away
POP P,T1 ; restore block address
CALL RETBLK ; Yes. Give back storage
GWYIN1: AOBJN I,GWYINL ; Do all GW blocks
JRST GWYIN4
; First time init
GWYIN2: MOVEI T1,MAXGWA ; Maximum number of gateways
CALL GETBLK ; Get a block of storage
JUMPE T1,GWYIN9 ; Crash
MOVEM T1,GWTAB
MOVEI T2,MAXGWA ; Size of the block
CALL CLRBLK ; Clear it out
GWYIN4:
; Each call
GWYIN5: CALL LODFIL ; Load the gateway file
CALL NETHSI ; Clear the gateway cache
CALL PINGER ; Ping the gateways
JRST GWYINX
GWYIN9: INBUG (HLT,<GWYINI: Crucial storage missing>,INGGP0)
GWYINX: POP P,GW
RET
PURGE I
; LODFIL() Load the gateway file
;
; CALL LODFIL
;Ret+1: Always
LODFIL: ACVAR <JFH,CHNS>
SETO JFH, ; Indicate nothing to release
MOVEI T1,.FHSLF ; This fork
RCM ; Get channels which are on
MOVEM T1,CHNS ; Save for restoring
MOVEI T1,.FHSLF
MOVX T2,1B<.ICEOF> ; End of file channel
DIC ; Prevent unwanted interrupt
MOVX T1,GJ%OLD+GJ%SHT ; Want existing file
HRROI T2,GWFILE ; Pointer to filename string
GTJFN
JRST LODFIX ; Not there
MOVEM T1,JFH
MOVX T2,<FLD(7,OF%BSZ)+OF%RD> ; Want to read it
OPENF
JRST LODFIX
CALL PRCLIN ; Process lines in the file
CLOSF
JFCL
LODFIX: SKIPL T1,JFH
RLJFN
JFCL
MOVEI T1,.FHSLF
MOVE T2,CHNS
AIC
RET
PURGE JFH,CHNS
; PRCLIN(JFH) Process lines of the gateway file
;T1/ JFH of the file
;
; CALL PRCLIN
;Ret+1: Always. T1 still has the JFH
PRCLIN::ACVAR <JFH,BOL,ERRPNT,ERRCOL>
MOVEM T1,JFH ; Stash JFH in a save place
; Top of main per-line loop:
PRCLI1: MOVE T1,JFH ; Get the file JFH
RFPTR ; Find out where in file line is
JFCL
MOVEM T2,BOL ; Save beginning of line
CALL GETC ; First character of line
JUMPE T2,PRCLIX ; get out if end of file
CAIN T2,12 ; Linefeed?
JRST PRCLI1 ; Ignore blank lines
CAIN T2,";"
JRST PRCLI8 ; Flush comment line
CAIN T2,"C"
JRST PRCLI7 ; Go do CREATION command
BKJFN ; Back up so LOADGW can read 1st chr
JFCL ; Will ITRAP on BIN if error in T1
CALL LOADGW ; Load a gateway description
JUMPE T2,PRCLI1 ; Do next if no error
; Here when error detected in current line (pointer to message in T2)
PRCLI2: MOVEI T1,.PRIOU
SETZ T3,
SOUT ; Type the error string
HRROI T2,[ASCIZ " in file: "]
SOUT
MOVE T2,JFH
JFNS ; And the actual file name
HRROI T2,[ASCIZ "
"]
SOUT ; And a carriage return
MOVE T1,JFH
RFPTR ; Find out where we have read to
JFCL
SOS ERRPNT,T2 ; Save the error point
MOVE T2,BOL ; Beginning of the bad line
SFPTR
JFCL
SETOM ERRCOL ; Maybe nothing read of line
; Top of loop that types out a bad line
PRCLI3: MOVE T1,JFH
RFPTR ; Get the file pointer
JFCL
CAME T2,ERRPNT ; Up to the point of the error
JRST PRCLI4 ; No. Dont save column yet
MOVEI T1,.PRIOU
RFPOS
HRRZM T2,ERRCOL ; Column where to show error
PRCLI4: MOVE T1,JFH
CALL GETC ; Get a character from bad line
SKIPN T2 ; End of file?
MOVEI T2,12 ; Yes. Use linefeed.
CAIN T2,12 ; End of line?
JRST PRCLI5 ; Yes. Done
MOVEI T1,.PRIOU
BOUT ; Type a character
JRST PRCLI3 ; Do next one
PRCLI5: MOVEI T1,.PRIOU
HRROI T2,[ASCIZ "
"]
SETZ T3,
SOUT ; Type and end of line terminal
JUMPLE ERRCOL,PRCLI6 ; Know where to show the error?
MOVEI T2," " ; Yes. Space over to it.
BOUT
SOJG ERRCOL,.-1 ; All the way.
PRCLI6: HRROI T2,[ASCIZ "↑
"]
SOUT
JRST PRCLI1 ; Try to finish the file
; Do CREATION command
PRCLI7: CALL GETC ; Skip over stuff following the C
MOVE T3,T2 ; Free up T2
HRROI T2,[ASCIZ "% INCMP: Premature EOF"]
JUMPE T3,PRCLI2 ; Go do the error if need be
CAIE T3," " ; One space is required separator
JRST PRCLI7 ; Loop til it is found
SETZ T2, ; Default flags
IDTIM ; Input the time and date
SKIPA T2,[-1,,[ASCIZ "% INCMP: Bad format in creation date"]]
MOVEM T2,GFCTAD ; Save our gateway file creation date
JUMPL T2,PRCLI2 ; Do error if need be
JRST PRCLI1 ; Do another command
; Here to flush a comment line
PRCLI8: CALL GETC ; Get a character
JUMPE T2,PRCLIX ; Get out if end of file
CAIE T2,12 ; End of line?
JRST PRCLI8 ; No.
JRST PRCLI1 ; Go read the next line.
PRCLIX: MOVE T1,JFH ; Preserve JFH as promised
RET
PURGE JFH,BOL,ERRPNT,ERRCOL
NR GFCTAD,1 ; Gateway file creation time and date
; LOADGW(JFH) Load one gateway desciption and add to table
;T1/ JFH
;
; CALL LOADGW
;Ret+1: Always. T2 has 0 if no error or -1,,errorstring
; T1 preserved.
LOADGW: ACVAR <JFH,EOLFLG>
TRVAR <<GWTMP,GWBKSZ>> ; Temp gateway block storage
PUSH P,GW
MOVEM T1,JFH
XMOVEI T1,GWTMP ; Point to the temp block
MOVE GW,T1 ; ...
MOVEI T2,GWBKSZ ; size thereof
CALL CLRBLK ; clear it
SETZM EOLFLG ; end of line not seen
; Top of per-keyword loop:
LOADG1: MOVE T1,JFH
CALL GETC ; Get a character
JUMPE T2,LOADG8 ; Oops. End of file.
CAIE T2,"." ; decimal number separator
CAIN T2," " ; Space (control, etc)
JRST LOADG1 ; Yes. Flush it.
CAIL T2,"0"
CAILE T2,"9"
JRST LOADG4 ; Non-digit. Must be keyword
; Here to input an interface address in N H L I form.
LOADG2: SETZM T4 ; Clear the number accumulator
BKJFN ; Reread the digit
JFCL
LODG2A: MOVEI T3,↑D10 ; Decimal
NIN
JRST LOADG7 ; Null number?
LSH T4,↑D8 ; Make room for another byte
ADD T4,T2 ; Add it in
BKJFN ; Reread the terminator
JFCL
BIN
CAIN T2,15 ; Happens on TENEX
BIN ; Get the line feed, like TOPS20
JUMPE T2,LOADG8 ; Jump if end of file encountered
CAIE T2,"." ; Dots separate bytes
CAIN T2," " ; Space means another byte follows
JRST LODG2A ; Go get it
CAIN T2,12 ; End of line?
SETOM EOLFLG ; Yes. Remember to exit later.
CAIE T2,12 ; End of line
CAIN T2,"," ; End of address expression?
JRST LOADG3 ; Yes. Go enter into GW block
JRST LOADG7 ; Anything else is bad format.
; Put address in temporary GW block.
LOADG3: LOAD T3,GWICT,(GW) ; Get current count
CAIL T3,MXGWIC ; Room for another?
JRST LOAD65 ; No.
ADDI T3,1 ; Bump the count
STOR T3,GWICT,(GW) ; Store back
ADDI T3,.GWILS-1 ; Offset to first empty slot
ADD T3,GW ; Where to store the address
MOVEM T4,0(T3) ; Insert interface address into GW block
SKIPN EOLFLG ; Read entire GW spec?
JRST LOADG1 ; No. Get another keyword/addr
JRST LOADG6 ; Yes. Go tie off this block
; Process a keyword
LOADG4: SETO T3, ; Keyword error flag
CAIN T2,"P" ; "PRIME"
MOVX T3,GW%PRM
CAIN T2,"D" ; "DUMB"
MOVX T3,GW%DUM
CAIN T2,"H" ; "HOST"
MOVX T3,GW%HST
CAIN T2,"A"
MOVX T3,GW%AUP ; "ALLWAYS-UP"
HRROI T2,[ASCIZ "% LOADGW: Unknown keyword "]
JUMPL T3,LOADGX ; Give error if invalid keyword
HRROI T2,[ASCIZ "% LOADGW: Too many gateway type specs."]
JN GWTYP,(GW),LOADGX ; Give error if already have spec
STOR T3,GWTYP,(GW) ; Set type into GW block
; Here to skip over the rest of the current keyword
LOADG5: CALL GETC ; Get a character
JUMPE T2,LOADG8 ; End of file?
CAIN T2,12 ; End of line?
JRST LOADG6 ; Yes. Go tie it off.
CAIE T2," " ; Space
CAIN T2,"," ; Or comma will end it
JRST LOADG1 ; Go read next keyword
JRST LOADG5 ; Keep reading the rest of this one
; Here to tie off the block which has been accumulating
LOADG6: CALL DEFGWY ; Create real gateway blocks
JRST LOADGX ; return with the result
; Error returns
LOAD65: CALL DEFGWY ; Create real gateway blocks
SKIPN T2 ; Double error
HRROI T2,[ASCIZ /% INCMP: Too many addresses in gateway description./]
JRST LOADGX ; return with the result
LOADG7: SKIPA T2,[-1,,[ASCIZ "% INGGP: Bad format "]]
LOADG8: HRROI T2,[ASCIZ "% INGGP: Premature end of file "]
LOADGX: MOVE T1,JFH
POP P,GW
RET
PURGE JFH,EOLFLG,GWTMP
; DEFGWY (GW) -- Create real gateway blocks
; given a gateway block pointer in GW, creates a real gateway block
; for each interface on a network we have in common with the gateway
;
; GW/ (ext) pointer to gateway block (in stack)
; CALL DEFGWY
;Ret+1: Always, T2/ 0 if ok, or
; -1,,pointer to error msg
DEFGWY: ACVAR <CIDX,CCNT,CSLT>
XMOVEI CIDX,.GWILS(GW) ; Point to the interface list
LOAD CCNT,GWICT,(GW) ; Interface count
JUMPE CCNT,DEFGX1 ; None (?)
JE GWTYP,(GW),DEFGX2 ; No type specified
;; Top of the loop
DEFGW0: MOVE T1,(CIDX) ; Get an interface from the table
CALL LCLNET ; have we an interface on the same net?
JRST DEFGW9 ; no
;;; Find a slot to store the gateway block in
MOVE CSLT,GWTAB ; point to the gateway table
MOVEI T4,MAXGWA ; size of the table
DEFGW1: SKIPN 0(CSLT) ; slot empty?
JRST DEFGW2 ; yes
XMOVEI CSLT,1(CSLT) ; increment pointer
SOJG T4,DEFGW1 ; Loop
HRROI T2,[ASCIZ /% INCMP: DEFGW -- GWTAB full/]
RET
DEFGW2: MOVEI T1,GWBKSZ ; size of a gateway block
CALL GETBLK ; get storage
JUMPE T1,DEFGX3 ; no storage
MOVEI T2,GWBKSZ ; Size of the block
PUSH P,T1 ; Save block address
CALL CLRBLK ; clear it
POP P,T1 ; get block address back
SETONE <GWUP,GWHIS>,(T1) ; Init history bits
MOVEI T2,WID(GWHIS) ; Number of bits in ping history
STOR T2,GWSPC,(T1) ; Set succesfull ping count to match
LOAD T2,GWTYP,(GW) ; Get gateway type
STOR T2,GWTYP,(T1) ; And save it
MOVE T2,(CIDX) ; Get interface we can reach
MOVEM T2,.GWILS(T1) ; Save
LOAD T3,GWICT,(GW) ; interface count
STOR T3,GWICT,(T1) ; Save here also
XMOVEI T2,.GWILS(GW) ; point to the list
PUSH P,T1 ; Save block
XMOVEI T1,.GWILS+1(T1) ; Point to interface list
DEFGW3: CAMN CIDX,T2 ; Same as current?
JRST DEFGW4 ; yes, on to next
MOVE T4,(T2) ; get an interface
MOVEM T4,(T1) ; save in block
XMOVEI T1,1(T1) ; increment block pointer
DEFGW4: XMOVEI T2,1(T2) ; increment source pointer
SOJG T3,DEFGW3 ; loop
POP P,T1 ; restore block pointer
MOVEM T1,(CSLT) ; save block in gateway table
; See if another interface on a common net
DEFGW9: XMOVEI CIDX,1(CIDX) ; increment interface pointer
SOJG CCNT,DEFGW0 ; try the next interface
SETZ T2, ; return good
RET ; return when done
;;; Error returns
DEFGX1: HRROI T2,[ASCIZ /% INCMP: DEFGW -- No interfaces for gateway/]
RET
DEFGX2: HRROI T2,[ASCIZ /% INCMP: No gateway type specified/]
RET
DEFGX3: HRROI T2,[ASCIZ /% INCMP: DEFGW -- No free storage for gateway block/]
RET
PURGE CIDX,CCNT,CSLT
; GETC(JFH) Get a character from a file
;T1/ JFH of the file
;
; CALL GETC
;Ret+1: T1 preserved. T2 has the chr or 0 if end of file
GETC: BIN ; Read the file
JUMPN T2,GETC2 ; Jump if a character gotten
GTSTS ; Read a null.
TXNN T2,GS%EOF ; At end of file?
JRST GETC ; No. Just flush the null
MOVEI T2,0 ; Set to return the EOF code
JRST GETCX
GETC2: CAIE T2,14 ; Formfeed?
CAIN T2,37 ; TENEX EOL?
MOVEI T2,12 ; Convert to linefeed
CAIN T2,12 ; Linefeed?
JRST GETCX ; Return that
CAIGE T2," " ; Other control?
JRST GETC ; Yes. Flush
CAIL T2,"a"
CAILE T2,"z"
CAIA ; Not lowercase
SUBI T2,"a"-"A" ; Raise lowercase
GETCX: RET
;;; Linkage to the routing routines
;;; FNDGWY -- Find a gateway with an interface on a given net.
;;; Called
;;; T1/ HOST number
;;; returns
;;; +1 always
;;; T1/ address of the best gateway to that net
;;; (if none directly connected can be found, then a random
;;; PRIME gateway is chosen)
;;; If no gateways or interfaces are up returns 0
FNDGWY::ACVAR <GWT,I,DEFGW>
SETZ DEFGW, ; No default gateway yet
MOVSI I,-MAXGWA ; Size of tables
NETNUM T2,T1 ; Get the network number
FNDGWL: HRRZ GWT,I ; Get offset
ADD GWT,GWTAB ; Point into table
SKIPN GWT,(GWT) ; Get entry (if any)
JRST FNDGW5 ; Slot is empty
JE GWUP,(GWT),FNDGW5 ; Gateway is not up
MOVE T1,.GWILS(GWT) ; Get accessable address
CALL NETCHK ; Is this interface up?
JRST FNDGW5 ; No, try another gateway
JUMPN DEFGW,FNDGW0 ; If we already have a default
LOAD T3,GWTYP,(GWT) ; Get type
CAIN T3,GW%PRM ; PRIME?
MOVE DEFGW,.GWILS(GWT) ; Get the accessable address
FNDGW0: LOAD T3,GWICT,(GWT) ; Get the interface count
XMOVEI T4,.GWILS(GWT) ; Point to interface names
FNDGW1: MOVE T1,(T4) ; Get an address
NETNUM T1,T1 ; Get the net number
CAME T1,T2 ; Same network as we want?
JRST FNDGW2 ; No
MOVE T1,.GWILS(GWT) ; Get the accessable address
RET ; and return
FNDGW2: AOS T4 ; Point to the next entry
SOJG T3,FNDGW1 ; and loop through this gateway
FNDGW5: AOBJN I,FNDGWL ; Loop through all gateway blocks
;;; Here if no gateway is perfect
MOVE T1,DEFGW ; get default gateway (0 if none)
RET ; no gateway found
PURGE GWT,I,DEFGW
; Top level ICMP Processing routine. Called from main Internet
; fork loop.
; (no args)
;
; CALL ICMPRC
;Ret+1: Always
ICMPRC::SETZM ICMFLG ; Clear run request flag
CALL ICMDSP ; Dispatch any msgs which are waiting
MOVE T1,PINGTM ; Time of next ping
CAMGE T1,TODCLK ; Over due?
CALL PINGER ; Yes. Do ping stuff.
MOVE T1,NETHTM ; Time to reinit the hash tables?
CAML T1,TODCLK ; ?
JRST ICMPR1 ; No, skip following
CALL NETHSI ; clear the tables
MOVE T1,TODCLK ; get time now
ADD T1,NETHT0 ; add in offset
MOVEM T1,NETHTM ; save
ICMPR1:
CAML T1,PINGTM ; use the minimum time
MOVE T1,PINGTM ; get time of next ping
MOVEM T1,ICMTIM ; save as when we have to run
RET
; Check Routine for ICMP Tells when to run next
;T1/ A TODCLK
;
; CALL ICMCHK
;Ret+1: Always. T1 has min of input T1 and when we should run next.
ICMCHK::CAMLE T1,ICMTIM ; Check against our next timeout
MOVE T1,ICMTIM ; That is sooner
RET
; PINGER() Ping gateways to see if they are alive
;
; CALL PINGER
;Ret+1: Always. PINGTM reset for next run
PINGER: ACVAR <I>
PUSH P,GW
MOVSI I,-MAXGWA ; Set to scan the gateway table
PINGE1: HRRZ GW,I ; Get offset into table
ADD GW,GWTAB ; Add base pointer
;; SKIPN INTSCR ; No pings if secure
SKIPN GW,0(GW) ; Get pointer to gateway
JRST PINGE8 ; Unoccupied slot
LOAD T1,GWHIS,(GW) ; Get the history bits
LOAD T2,GWSPC,(GW) ; Get the successful ping count
TRNE T1,1 ; Test bit about to be forgotten
SUBI T2,1 ; Forgetting a success
SKIPGE T2 ; Avoid negative while down
MOVEI T2,0 ; This is as bad as you can get
LOAD T3,GWPIP,(GW) ; See if previous ping still in progress
XORI T3,1 ; Flip sense to indicate success
LSH T3,WID(GWHIS)-1 ; Move to left end
LSH T1,-1 ; Flush the oldest history bit
IOR T1,T3 ; Include in history bits
SKIPE T3 ; Did we add a success to the list
ADDI T2,1 ; Yes. Count it up
CAILE T2,WID(GWHIS) ; Check for overflow
MOVEI T2,WID(GWHIS) ; Limit to max
STOR T2,GWSPC,(GW) ; Store back the count
STOR T1,GWHIS,(GW) ; Store back the bits
LOAD T4,GWUP,(GW) ; Get current state
MOVE T3,T4 ; Save a copy
CAIL T2,.THRUP ; Enough success to say it's up?
MOVEI T4,1 ; Yes
CAIG T2,.THRDN ; So few that it is down?
MOVEI T4,0 ; Yes.
STOR T4,GWUP,(GW) ; Set new value
XOR T3,T4 ; Compare to see if change
JUMPE T3,PINGE7 ; Jump if no change
JUMPN T4,PINGE7 ; Jump if it came up
CALL GWDOWN ; Yes. Flush from tables now.
PINGE7: SETONE GWPIP,(GW) ; Set ping-in-progress bit
CALL SNDPNG ; Send a ping to guy in GW
PINGE8: AOBJN I,PINGE1 ; Loop over all gateways
MOVE T1,PINGT0 ; Interping interval
ADD T1,TODCLK ; Time of next ping/check
MOVEM T1,PINGTM ; Save for scheduling
POP P,GW
RET
PURGE I
;SNDPNG Send a ping message to a GW
; This is an ECHO message if we are sending to a PRIME gateway, or
; an ECHO REPLY addressed to ourself if testing a non-PRIME
; gateway. Net result is that we get back only ECHO REPLIES.
;GW/ Pointer to gateway block
;
; CALL SNDPNG
;Ret+1: Always.
SNDPNG:
IFN MNET,<SAVP1>
PUSH P,PKT
PUSH P,CPKT
LOAD T1,GWTYP,(GW) ; Gateway type code
CAIN T1,GW%AUP ; Always up?
JRST SNDPNU ; Yes, go fake a successful ping
; Must actually send a ping
MOVEI T1,MINICW ; Size of echo packet
CALL GETBLK ; Get storage in which to build pkt
SKIPN PKT,T1 ; Put in standard place
JRST SNDPNU ; Not available. Don't let it go down.
MOVEI T2,MINICW ; Size again
CALL CLRBLK ; Clear all flags, checksum, etc
MOVE T1,[BYTE (8)105,0,0,<8+MINIHS>]
MOVEM T1,PKTELI+.IPKVR(PKT) ; Set version, length
MOVEI T1,3 ; Ping "lifetime"
STOR T1,PITTL,(PKT)
MOVEI T1,.ICMFM ; Protocol is ICMP
STOR T1,PIPRO,(PKT)
MOVEI CPKT,<<MINIHS+3>/4>+PKTELI ; Min. Internet header size
ADD CPKT,PKT ; Pointer to ICMP Section
MOVE T1,.GWILS(GW) ; Get accessable interface
CALL GWYLUK ; Look it up
JUMPE T1,SNDPNW ; No way to that net??
IFE MNET,<MOVE T2,NLHOST(T3)> ; Get our address
IFN MNET,<MOVE T2,NTLADR(P1)> ; get interface address
MOVEI T3,ICM%EC ; Echo type
LOAD T4,GWTYP,(GW) ; Get type
CAIN T4,GW%PRM ; Prime?
JRST SNDPN6 ; Yes
EXCH T1,T2 ; Swap source and destination
MOVEI T3,ICM%ER ; ECHO-REPLY code
SETONE PNLCL,(PKT) ; No local delivery allowed
SNDPN6:
STOR T2,PISH,(PKT) ; Make it look like it came from there
STOR T1,PIDH,(PKT) ; Make it go there
STOR T3,CMTYP,(CPKT) ; Set into ICMP section
SETZRO CMCOD,(CPKT) ; Clear code word
SETONE CMID,(CPKT) ; (Make field non-zero)
AOS T1,ICMSID ; Get an Id
STOR T1,CMSEQ,(CPKT)
STOR T1,PISID,(PKT)
CALL ICMCKS ; Compute checksum
STOR T1,CMCKS,(CPKT) ; Insert in packet
CALL SNDGAT ; Send it off
JRST SNDPNX
; Error exits
SNDPNU: SETZRO GWPIP,(GW) ; Fake a successful ping
JRST SNDPNX
SNDPNW: CALL RETPKT ; Don't have anywhere to send packet
SNDPNX: POP P,CPKT
POP P,PKT
RET
;GWDOWN Gateway just detected down
; Called by the PINGER and in response to a local net
; "destination dead" message
;GW/ Pointer to gateway block
;
; CALL GWDOWN
;Ret+1: Always. GWUP bit cleared and all interfaces removed from tables
GWDOWN: MOVE T1,.GWILS(GW) ; Get the relevant interface
CALL GWYDWN ; say it went away
RET
;; ENDAV.
; ICMDSP Dispatch on ICMP message type
;
; CALL ICMDSP
;Ret+1: Always.
ICMDSP:
IFN MNET,<SAVP1>
PUSH P,PKT
PUSH P,CPKT
PUSH P,TCB
SETZ TCB,
ICMDS1: MOVE T1,ICMIPQ ; Pointer to input queue head
LOAD PKT,QNEXT,(T1) ; Get first thing on queue
SETSEC PKT,INTSEC ; Make extended address
CAMN PKT,T1 ; Pointer to head means empty
JRST ICMDSX ; Empty
MOVE T1,PKT ; What to dequeue
CALL DQ ; Get it off queue (NOSKED not needed)
MOVX T1,PT%CDI ; ICMP dequeued from input queue
TDNE T1,INTTRC ; Want trace?
CALL PRNPKI ; Yes
CALL ICMCKS ; Check ICMP Checksum
JUMPN T1,ICMDSC ; Jump if bad
LOAD CPKT,PIDO,(PKT) ; Internet data offset
ADD CPKT,PKT ; Get pointer to ICMP portion
ADDI CPKT,PKTELI ; Skip over local information
LOAD T1,CMTYP,(CPKT) ; What kind of message it is
MOVSI T2,-NICMPT ; Number of messages we know about
CAME T1,ICMTTB(T2) ; Matches this one?
AOBJN T2,.-1 ; No. Try next.
JUMPGE T2,ICMDST ; Jump if not found
LOAD T3,PIPL,(PKT) ; Packet length in bytes
LOAD T4,PIDO,(PKT) ; Internet data offset in words
ASH T4,2 ; Make that bytes
SUB T3,T4 ; Number of bytes in ICMP part
SUB T3,ICMMDC(T2) ; Minus min number req'd for this type
JUMPL T3,ICMDSS ; Enough in packet?
CALL @ICMRTB(T2) ; Yes, call routine; skips if keeping pkt
ICMDS8: CALL RETPKT ; Return the packet to free storage
JRST ICMDS1 ; Loop through rest of Q
; Errors
ICMDSC: MOVX T1,PT%CKC ; Checksum failure
JRST ICMDS9
ICMDSS: MOVX T1,PT%CKS ; Short packet
JRST ICMDS9
ICMDST: MOVX T1,PT%CKT ; Unknown type
; JRST ICMDS9
ICMDS9: TDNE T1,INTTRC ; Want trace?
CALL PRNPKI ; Yes
AOS BADPCT ; Increment bad packet count
JRST ICMDS8 ; and loop
ICMDSX: POP P,TCB
POP P,CPKT
POP P,PKT
RET
; Table of type codes (ordered by frequency):
ICMTTB: ICM%ER ; Echo reply
ICM%EC ; Echo
ICM%DU ; Destination unreachable
ICM%RD ; Redirect output
ICM%SQ ; Source quench
ICM%PP ; Parameter problem
ICM%TE ; Time exceeeded
ICM%TM ; Time stamp
ICM%TR ; Time stamp reply
ICM%IQ ; Information request
ICM%IR ; Information reply
NICMPT==.-ICMTTB ; Number of types we know about
; Action routines table parallel to the above:
ICMPDU==ICMUSR
ICMPTR==ICMUSR
ICMPIR==ICMUSR
ICMRTB: IFIW!ICMPER ; process echo reply
IFIW!ICMPEC ; process echo
IFIW!ICMPDU ; process destination unreachable
IFIW!ICMPRD ; process redirect
IFIW!ICMUSR ; process source quench
IFIW!ICMUSR ; process parameter problem
IFIW!ICMUSR ; process time exceeded
IFIW!ICMPTM ; process timestamp request
IFIW!ICMPTR ; process timestamp reply
IFIW!ICMPIQ ; process information request
IFIW!ICMPIR ; process information reply
IFN .-ICMRTB-NICMPT,<PRINTX ? ICMP dispatch tables have wrong size>
; Table of minimum data counts (in bytes)
; (if greater than 8+24, then an internet header and 64 bits of data
; is part of the packet)
ICMMDC: 8 ; Echo reply length
8 ; Echo length
8+MINIHS+8 ; Destination unreachable
8+MINIHS+8 ; Redirect
8+MINIHS+8 ; Source quench
8+MINIHS+8 ; Parameter problem
8+MINIHS+8 ; Time exceeded
8+24 ; Timestamp request
8+24 ; Timestamp reply
8 ; Information request
8 ; Information reply
IFN .-ICMMDC-NICMPT,<PRINTX ? ICMP tables screwed up>
; Process an ECHO message:
ICMPEC: MOVX T1,ICM%ER ; Echo reply code
JRST ICMPEX ; Swap & send
; Process Timestamp request
ICMPTM: CALL INETUT ; Get universal time
STOR T1,CMTSR,(CPKT) ; Not really right
STOR T1,CMTST,(CPKT) ; Transmission time
MOVX T1,ICM%TR ; Timestamp reply code
JRST ICMPEX ; Swap & send
; Process Information request
ICMPIQ: MOVX T1,ICM%IR ; Information reply code
IFE MNET,<MOVE T2,INETID> ; Our default address
IFN MNET,<MOVE T2,DEFADR>
JRST ICMPEY
; Common exit for replies
ICMPEX: LOAD T2,PIDH,(PKT) ; Destination (us)
ICMPEY: LOAD T3,PISH,(PKT) ; Source (who wants echo)
STOR T1,CMTYP,(CPKT) ; Set as the ICMP type code
STOR T2,PISH,(PKT) ; We are the echoer
STOR T3,PIDH,(PKT) ; Sender is the echoee
SETZRO CMCKS,(CPKT) ; Clear checksum
CALL ICMCKS ; do checksum
STOR T1,CMCKS,(CPKT) ; set it
CALL SNDGAT ; Send it back
RETSKP
; Process an ECHO-REPLY message:
ICMPER: MOVX T1,<.RTJST(-1,CMID)> ; We set it to -1
LOAD T2,CMID,(CPKT) ; What's in packet?
CAME T1,T2 ; Reply for us or a user?
JRST ICMUSR ; Not one PINGER sent, give to user
PUSH P,GW
LOAD T1,PISH,(PKT) ; Who it appears to be from (maybe us)
CALL FINDGW ; Look up the gateway block
JUMPE GW,ICERX ; Not there
SETZRO GWPIP,(GW) ; Clear ping-in-progress bit
ICERX: POP P,GW
RET
; Process a REDIRECT message
; The destination that triggered the message is in the
; "trigger header"
ICMPRD:
ACVAR <GWY>
LOAD T1,CMCOD,(CPKT) ; get the code
CAIE T1,RD%NET ; re-direct net?
CALLRET ICMUSR ; No, the rest must be handled by the user
LOAD T1,CMGWA,(CPKT) ; And the correct gateway address
IFN MNET,<
CALL FNDNCT ; Get the NCT for that net
RET ; NO? ignore it
LOAD T2,NTNUM,(P1) ; Get the interface index
>
IFE MNET,<
PRINTX ? INCMP: ICMPRD needs to find interface index somehow
>
STOR T2,INTNUM,+T1 ; Save in the address
MOVE GWY,T1 ; Save address
LOAD T1,PIDH,-PKTELI+.CMINH(CPKT); get the triggering destination host
NETNUM T2,T1 ; Get the network number
CALL NETHSH ; Hash it
CAIA ; Not currently in tables (cache flushed)
; Local net unreachable mean partitioned??
SKIPL NETGWY(T2) ; A local net?
MOVEM GWY,NETGWY(T2) ; No, set this as the gateway
RET ; and return
PURGE GWY
;;; Process an UNREACHABLE message
;;; This means that all paths into the indicated network (host, port etc.)
;;; are broken. There is no need to change the routing tables,
;;; since they would be re-created anyway, and if the net comes back
;;; via another path we will get a redirect message.
;;; Therefore all we need to do is notify the user (so s/he can break
;;; the connection if s/he wishes)
;ICMPDU:
; FINDGW Set GW to point to gateway block with address in T1
;T1/ An interface address
;
; CALL FINDGW
;Ret+1: Always. GW has pointer to block or 0 if not found
FINDGW: ACVAR <GWX>
MOVSI GWX,-MAXGWA ; Size of table
FINDG1: HRRZ GW,GWX ; Get table offset
ADD GW,GWTAB ; Add base
SKIPN GW,0(GW) ; Get pointer to gateway block
JRST FINDG9 ; Empty slot
CAMN T1,.GWILS(GW) ; This gateway?
JRST FINDGX ; yes, exit with it
FINDG9: AOBJN GWX,FINDG1 ; Try next gateway
MOVEI GW,0 ; Indicate failure
FINDGX: RET
PURGE GWX
;; ENDAV.
;;;
;;; ICMUSR -- Give an ICMP message to the user.
;;; There are two possibilities:
;;; If the message is in response to a user Q message,
;;; we simply stick it on that Q's recieve Q (and let the
;;; user process it)
;;; If the message is in response to a monitor protocal (TCP)
;;; then we have to call the proper routines for handling
;;; it.
;;; Called
;;; PKT/ Packet pointer
;;; CPKT/ ICMP portion
;;; (at all times, if we reach this routine, the ICMP data
;;; contains the Internet header that triggered the message
;;; starting at .CMINH(CPKT) )
;;; *** NOT FULLY IMPLIMENTATED ****
ICMUSR: ACVAR <PIX,PTB,PTL>
MOVEI PTB,INTPIX+1 ; Locate tables
MOVE PIX,INTPIX ; # protocals
HRRZ PTL,PIX ; Table length
LOAD T1,PIPRO,-PKTELI+.CMINH(CPKT) ; Get triggering protocal
ICMUS0: SKIPN .INTPO(PTB) ; Protocal on?
JRST ICMUS3 ; No, skip it
SKIPL T2,.INTPL(PTB) ; Take any protocal?
CAMN T1,T2 ; Or match?
CALLRET @.INTPE(PTB) ; Enter the proper routine
;;; Routine returns (to ICMDS8) +2 if it has kept the packet
ICMUS3: ADD PTB,PTL ; Increment pointer
AOBJN PIX,ICMUS0 ; And loop
;;; Here if nobody to accept the packet
RET ; Return so it will be released.
;;; Here to process an ICMP message for ICMP
ICMICM::RET ; Shouldn't happen
;;; Dummy routine to handle an ICMP message for TCP
;;; (** remove when implimented **)
TCPICM::RET
PURGE PIX,PTB,PTL
;;; ICMERR -- Handle an error. Called with
;;;
;;; T1/ ICMP error code (LH == subcode if any)
;;; T2/ Additional info, if any (parameter problem pointer)
;;; PKT/ Erring packet
;;; CALL ICMERR
;;; Ret+1: Always, packet returned if PINTL and PPROG were both zero
ICMERR::ACVAR <ICMIDX,INFO> ; AC variables
TRVAR <ERROR,HDRSIZ> ; Stack variables
PUSH P,CPKT ; Save register we clobber
SETZ CPKT, ; Clear
MOVEM T1,ERROR ; Save erorr code
MOVEM T2,INFO ; and additional info
LOAD T2,PIPRO,(PKT) ; Get protocal
CAIN T2,.ICMFM ; Internet control message format?
JRST ICMERX ; Yes, Ignore the packet
HRRZS T1 ; Keep only the ICMP type
MOVSI ICMIDX,-NICMPT ; Number of ICMP types we handle
CAME T1,ICMTTB(ICMIDX) ; Same?
AOBJN ICMIDX,.-1 ; Loop through the table
JUMPGE ICMIDX,[INBUG(HLT,<ICMERR -- Bad type code>,ICMBDE)]
SETZM HDRSIZ ; Assume don't include header
MOVX T1,↑D<8+24> ; Size of data if no header
CAMLE T1,ICMMDC(ICMIDX) ; Same?
JRST ICMER0 ; Yes, no header, skip next
LOAD T1,PIDO,(PKT) ; Get data offset
ADDI T1,2 ; Plus 64 bits of data
MOVEM T1,HDRSIZ ; Remember it (words)
ICMER0:
MOVX T1,MINICW ; Buffer size w/o header
ADD T1,HDRSIZ ; Plus size of header (if needed)
JN <PINTL,PPROG>,(PKT),ICMER1 ; Program still need packet?
LOAD T2,PIPL,(PKT) ; This buffer big enough to reuse?
LSH T2,-2 ; Its size, words, rounded down
MOVE T3,HDRSIZ ; Beware BLT over self
; ADDI T3,PKTELI ; Last word to be copied
; CAIG T3,PKTELI+<<MINIHS+3>/4>+.CMINH ; Vs first word written
CAIG T3,<<MINIHS+3>/4>+.CMINH ; Vs first word written
CAILE T1,PKTELI(T2) ; Req'd vs actual
CAIA ; Need more than have or overwrite
JRST ICMER2 ; Reuse this packet, go shift
ICMER1: CALL GETBLK ; Get new packet
JUMPE T1,ICMERX ; No storage, do nothing
SKIPA CPKT,T1 ; Save packet
ICMER2: MOVE CPKT,PKT ; Re-use the packet buffer
; NB: CPKT is not pointing at ICMP header, but at packet
SKIPN T1,HDRSIZ ; Include header?
JRST ICMER3 ; No, skip next
XMOVEI T2,PKTELI(PKT) ; Start of internet leader
XMOVEI T3,PKTELI+<<MINIHS+3>/4>+.CMINH(CPKT) ; Where to stash it
CALL XBLTA ; move the data
ICMER3:
LOAD T1,PISH,(PKT) ; Get the packet source host
STOR T1,PIDH,(CPKT) ; Save as error destination
IFE MNET,<MOVE T1,INETID> ; Get default internet ID
IFN MNET,<MOVE T1,DEFADR>
STOR T1,PISH,(CPKT) ; Save it also
MOVE T1,[BYTE (8)105,0,0,0] ; First word of packet
MOVEM T1,PKTELI+.IPKVR(CPKT) ; Save
SETZM PKTELI+.IPKSG(CPKT) ; clear segmentation info
MOVE T1,[BYTE (8)12,.ICMFM,0,0] ; Time to live, protocal
MOVEM T1,PKTELI+.IPKPR(CPKT) ; Save it
MOVE T1,HDRSIZ ; Get header size included
ADDI T1,<<MINIHS+3>/4>+.CMINH ; Plus Internet & ICMP header
ASH T1,2 ; Convert to bytes
STOR T1,PIPL,(CPKT) ; Save packet length
PUSH P,PKT ; Save old packet
PUSH P,CPKT ; ...
MOVE PKT,CPKT ; Get new
ADDI CPKT,PKTELI+<MINIHS+3>/4 ; Point to the ICMP section
HRRZ T1,ERROR ; Get error type
STOR T1,CMTYP,(CPKT) ; Save type
HLRZ T2,ERROR ; Get code
STOR T2,CMCOD,(CPKT) ; Save it
SETZM 1(CPKT) ; Unused word
CAIN T1,ICM%PP ; Parameter problem?
CAIE T2,PP%PTR ; With pointer?
JRST ICMER5 ; No
STOR INFO,CMPTR,(CPKT) ; Yes, set pointer
JRST ICMER9
ICMER5:
CAME T1,ICM%TM ; Time request?
JRST ICMER6 ; No
CALL INETUT ; Get time
STOR T1,CMTSO,(CPKT)
SETZRO CMTSR,(CPKT)
SETZRO CMTST,(CPKT)
MOVX T1,<MINIHS+5*4>
LOAD T1,PIPL,(PKT)
; STOR T1,PIPL,(PKT)
JRST ICMER9
ICMER6:
ICMER9: AOS T1,ICMSID ; Get an Id
STOR T1,CMSEQ,(CPKT)
STOR T1,PISID,(PKT)
CALL ICMCKS ; Checksum the packet
STOR T1,CMCKS,(CPKT) ; Save it also
CALL SNDGAT ; Send it off
POP P,CPKT ; Restore old contents
POP P,PKT ; Restore old contents
ICMERX: JN <PINTL,PPROG>,(PKT),ICMERZ ; Return without destroying packet
CAME PKT,CPKT ; Don't release if re-used
CALL RETPKT ; and return storage
ICMERZ:
POP P,CPKT ; restore
RET ; return
PURGE ICMIDX,ERROR,INFO,HDRSIZ
TNXEND
END